﻿/*****************************************************************************\
 *             University of Illinois/NCSA Open Source License
 *
 * 
 *      Copyright © 2012,2013, The Board of Trustees of the University of Illinois. All rights reserved.
 *
 *      Developed by: Rakesh Bobba, Derek Dagit, Erich Heine, Denis Kholine,
 *                    Klara Nahrstedt, David Rodgers, William H. Sanders, Tim Yardley
 *
 *      Information Trust Institute/Coordinated Science Laboratory
 * 
 *      University of Illinois
 *      
 *      The Board of Trustees of the University of Illinois
 *      109 Coble Hall
 *      801 South Wright Street
 *      Champaign, IL 61820-6242
 *
 *      http://www.iti.illinois.edu / http://csl.illinois.edu
 * 
 *      Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
 *      associated documentation files (the “Software”), to deal with the Software without restriction, 
 *      including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *      and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 *      subject to the following conditions:
 *
 *      Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *      following disclaimers.
 *      
 *      Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *      the following disclaimers in the documentation and/or other materials provided with the distribution.
 *
 *      Neither the names of Information Trust institute, Coordinated Science Laboratory, University of Illinois,
 *      The Board of Trustees of the University of Illinois nor the names of its contributors may be used to 
 *      endorse or promote products derived from this Software without specific prior written permission.
 *      
 *      THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 *      LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN
 *      NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 *      SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 *      
 \*****************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SIEGate.Core.Stats
{
    /// <summary>
    /// A group of stats, to be stored in the object the Stats.Cluster is for.
    /// </summary>
    public class Cluster
    {
        protected readonly Dictionary<Type, Dictionary<string, StatInst>> m_statInstances = new Dictionary<Type, Dictionary<string, StatInst>>();
        /// <summary>
        /// The ID of the object with which the cluster is affiliated.
        /// </summary>
        uint ID;

        /// <summary>
        /// Instantiate a new Cluster.
        /// </summary>
        /// <param name="id">The ID of the object with which this cluster is affiliated.</param>
        // TODO: Change this to internal.  It should always be called from RegisterCluster in Stats.  Not doing now because that messes up a lot of tests.
        public Cluster(uint id)
        {
            ID = id;
        }

        protected virtual StatInst Register(Func<string> getOwnerName, string statName, Type statType, params object[] args)
        {
            if (!m_statInstances.ContainsKey(statType))
            {
                m_statInstances.Add(statType, new Dictionary<string, StatInst>());
            }
            if (m_statInstances[statType].ContainsKey(statName))
            {
                return m_statInstances[statType][statName];
            }
            StatInst result = Stats.StatDispatcher.InstantiateStat(getOwnerName, statName, statType, args);
            m_statInstances[statType].Add(statName, result);
            return result;
        }

        public Counter RegisterCounter(string ownerName, string statName)
        {
            return RegisterCounter(() => ownerName, statName);
        }

        public Counter RegisterCounter(Func<string> getOwnerNameFunction, string statName)
        {
            
            return (Counter) Register(getOwnerNameFunction, statName, typeof(Counter));
        }

        public DelegateCounter RegisterDelegateCounter(string ownerName, string statName, Func<long> function)
        {
            return RegisterDelegateCounter(() => ownerName, statName, function);
        }

        public DelegateCounter RegisterDelegateCounter(Func<string> getOwnerNameFunction, string statName, Func<long> function)
        {

            return (DelegateCounter)Register(getOwnerNameFunction, statName, typeof(DelegateCounter), function);
        }

        public DelegateDoubleGenerator RegisterDelegateDoubleGenerator(string ownerName, string statName, Func<double> function)
        {
            return RegisterDelegateDoubleGenerator(() => ownerName, statName, function);
        }

        // We need a version of this function that takes a statDispatcher as an argument to avoid stack overflow in the Stats() constructor
        internal DelegateDoubleGenerator RegisterDelegateDoubleGenerator(Func<string> getOwnerNameFunction, string statName, Func<double> function)
        {
           
            return (DelegateDoubleGenerator)Register( getOwnerNameFunction,statName, typeof(DelegateDoubleGenerator), function);
        }

        public Duration RegisterDuration(string ownerName, string statName)
        {
            return RegisterDuration(() => ownerName, statName);
        }

        public Duration RegisterDuration(Func<string> getOwnerNameFunction, string statName)
        {
          
            return (Duration)Register(getOwnerNameFunction, statName, typeof(Duration));

        }

        public new string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("{ ");
            builder.Append(string.Join(", ",
                                       string.Join(", ", m_statInstances.AsEnumerable()
                                                      .Select(
                                                          pair => pair.Key + " => { " + 
                                                              string.Join(", ", pair.Value.AsEnumerable().Select(
                                                              valuePair =>
                                                              valuePair.Key + " => ( " + valuePair.Value.ToString() + " )")) + " }"))));
            builder.Append(" }");
            return builder.ToString();
        }

        public Counter GetCounter(string statName)
        {
            return m_statInstances.ContainsKey(typeof(Counter)) && m_statInstances[typeof(Counter)].ContainsKey(statName) 
                ? (Counter)m_statInstances[typeof(Counter)][statName] : null;
        }

        public long GetCounterValue(string statName)
        {
            return GetCounter(statName).CurrentAggregateValue;
        }

        public Counter GetDelegateCounter(string statName)
        {
            return m_statInstances.ContainsKey(typeof(DelegateCounter)) && m_statInstances[typeof(DelegateCounter)].ContainsKey(statName)
                ? (Counter)m_statInstances[typeof(DelegateCounter)][statName] : null;
        }

        public long GetDelegateCounterValue(string statName)
        {
            return GetDelegateDoubleGenerator(statName).CurrentAggregateValue;
        }

        public DelegateDoubleGenerator GetDelegateDoubleGenerator(string statName)
        {
            return m_statInstances.ContainsKey(typeof(DelegateDoubleGenerator)) && m_statInstances[typeof(DelegateDoubleGenerator)].ContainsKey(statName)
                ? (DelegateDoubleGenerator)m_statInstances[typeof(DelegateDoubleGenerator)][statName] : null;
        }

        public float GetDelegateDoubleGeneratorValue(string statName)
        {
            return GetDelegateDoubleGenerator(statName).CurrentAggregateValue;
        }

        public Duration GetDuration(string statName)
        {
            return m_statInstances.ContainsKey(typeof(Duration)) && m_statInstances[typeof(Duration)].ContainsKey(statName)
                ? (Duration)m_statInstances[typeof(Duration)][statName] : null;
        }


        public TimeSpan GetDurationValue(string statName)
        {
            return GetDuration(statName).CurrentAggregateValue;
        }
        public TimeSpan GetDurationValueSinceLastReset(string statName)
        {
            return GetCounter(statName).SinceLastIntervalValue;
        }

        public void AddToCounter(string statName, long toAdd)
        {
            Counter counter = GetCounter(statName);
            if (counter != null)
            {
                counter.Add(toAdd);
            }
        }

        public StatInst GetStatInst(string statName)
        {
            foreach (Type t in m_statInstances.Keys)
            {
                if (m_statInstances[t].ContainsKey(statName))
                {
                    return m_statInstances[t][statName];
                }
            }
            return null;
        }

        public IEnumerable<StatInst> GetStatInsts()
        {
            foreach (Type t in m_statInstances.Keys)
            {
                foreach (StatInst statInst in m_statInstances[t].Values)
                {
                    yield return statInst;
                }
            }
        }

        public void ResetCounter(string statName)
        {
            if (m_statInstances.ContainsKey(typeof (Counter))
                && m_statInstances[typeof(Counter)].ContainsKey(statName))
            {
                m_statInstances[typeof (Counter)][statName].GetValueSinceLastResetAndReset();
            }
        }

        public void ResetDuration(string statName)
        {
            if (m_statInstances.ContainsKey(typeof (Duration))
                && m_statInstances[typeof(Duration)].ContainsKey(statName))
            {
                m_statInstances[typeof(Duration)][statName].GetValueSinceLastResetAndReset();
            }
        }
    }
}
